package net.xo68.destiny.core.timingwheel.process;

import lombok.extern.slf4j.Slf4j;
import net.xo68.destiny.core.timingwheel.Timeout;
import net.xo68.destiny.core.timingwheel.TimingWheel;
import net.xo68.destiny.core.timingwheel.handle.TimeoutEventHandler;
import net.xo68.destiny.core.util.TimeUtils;

import java.io.Serializable;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 超时事件处理器实现
 * @author wuxie
 * @version 2020-10-23
 * @param <T>
 */
@Slf4j
public class TimeoutEventProcessorImpl<T extends Serializable> implements TimeoutEventProcessor<T> {

    private static final int IDLE = 0;
    private static final int HALTED = IDLE + 1;
    private static final int RUNNING = HALTED + 1;

    private final AtomicInteger running = new AtomicInteger(IDLE);

    private final TimingWheel<T> timingWheel;
    private final EventProcessorContext<T> processorContext;
    private final Timeout<T> timeout;

    public TimeoutEventProcessorImpl(TimingWheel<T> timingWheel, Timeout<T> timeout) {
        this.timingWheel = timingWheel;
        this.timeout=timeout;
        this.processorContext=new EventProcessorContext<>(this.timeout);
    }

    public TimingWheel<T> getTimingWheel() {
        return timingWheel;
    }

    private TimeoutEventHandler<T> eventHandler(){
        return this.timingWheel.getEventHandler();
    }

    @Override
    public void halt() {
        running.set(HALTED);
    }

    @Override
    public boolean isRunning() {
        return running.get() != IDLE;
    }

    @Override
    public EventProcessorContext<T> getProcessorContext() {
        return this.processorContext;
    }

    @Override
    public void run() {
        if (running.compareAndSet(IDLE, RUNNING)){
            notifyStart();
            try
            {
                if (running.get() == RUNNING)
                {
                    processEvents();
                }
            }
            finally
            {
                notifyComplete();
                running.set(IDLE);
            }
        }else
        {
            this.timeout.remove();
            // This is a little bit of guess work.  The running state could of changed to HALTED by
            // this point.  However, Java does not have compareAndExchange which is the only way
            // to get it exactly correct.
            if (running.get() == RUNNING)
            {
                throw new IllegalStateException("Thread is already running");
            }
            else
            {
                log.error("线程正在运行");
            }
        }
    }

    /**
     * 处理事件，触发处理者事件
     */
    private void processEvents(){
        try{
            this.eventHandler().onEvent(this.getProcessorContext());
        }catch (Exception e){
            log.error(e.getMessage(),e);
        }

    }

    /**
     * 开始通知
     */
    private void notifyStart(){
        try{
            this.getProcessorContext().setStartTime(TimeUtils.nowLocalDateTime());
            this.eventHandler().onBefore(this.getProcessorContext());
        }catch (Exception e){
            log.error(e.getMessage(),e);
        }

    }

    /**
     * 完成通知
     */
    private void notifyComplete(){
        try{
            this.getProcessorContext().setFinishTime(TimeUtils.nowLocalDateTime());
            this.eventHandler().onComplete(this.getProcessorContext());
        }catch (Exception e){
            log.error(e.getMessage(),e);
        }finally {
            this.timeout.remove();
        }
    }
}
